<?php

namespace Haozing\FastCore\Utils\OpenDoc;

use Hyperf\Context\ApplicationContext;
use Hyperf\Contract\ConfigInterface;
use Hyperf\HttpServer\Router\DispatcherFactory;
use InvalidArgumentException;
use Hyperf\Di\Annotation\AnnotationCollector;

class ApiService
{
    private array $apps = [];
    /**
     * 获取所有应用的接口
     */
    public function getAllApiMenus(): array
    {
        $apiClassDocs = AnnotationCollector::getClassesByAnnotation(\Haozing\FastCore\Annotation\OpenDoc\ApiTitle::class);
        foreach ($apiClassDocs as $class => $doc) {
            // 判断$class 是否是Plugins、Internal、Apps开头，只有符合的才进行下一步
            if (str_starts_with($class, 'Plugins') || str_starts_with($class, 'Internal') || str_starts_with($class, 'Apps')) {
                // 如果开头是Apps。将$class按照\分割，取数组的前三个作为应用名称。其他取前两个
                $parts = explode('\\', $class);

                // 检查字符串是否以"Apps"开头
                if (str_starts_with($class, 'Apps')) {
                    // 如果是，取数组的前三个元素
                    $appName = implode('\\', array_slice($parts, 0, 3));
                } else {
                    // 如果不是，取数组的前两个元素
                    $appName = implode('\\', array_slice($parts, 0, 2));
                }

                //读取应用下的Open目录中的ppinfo.php类
                $appClass = $appName . '\\Open\\ppInfo';
                if (class_exists($appClass)) {
                    $appInfo = new $appClass();
                    $appInfo = $appInfo->getInfo();
                    $appTitle = $appInfo['name'];
                    //查看apps中是否存在appName
                    if (array_key_exists($appName, $this->apps)) {
                        //如果存在，将$appTitle添加到child数组中
                        $this->apps[$appName]['child'][$class] = [
                            'title' => $doc->toArray(),
                            'class' => $class,
                        ];
                    } else {
                        //如果不存在，创建一个新数组，将$appName和$appTitle添加到其中
                        $this->apps[$appName] = [
                            'title' => $appTitle,
                            'name' =>$appName,
                        ];
                        $this->apps[$appName]['child'][$class] = [
                            'title' => $doc->toArray(),
                            'class' => $class,
                        ];
                    }
                }
            }
        }

        $this->classHandle("ApiAuthor");
        $this->classHandle("ApiDesc");
        $this->classHandle("ApiHeader");
        $this->classHandle("ApiQuery");
        $this->classHandle("ApiParam");
        $this->classHandle("ApiType");
        $this->classHandle("ApiError");
        $this->classHandle("ApiSuccess");
        $this->classHandle("ApiReturn");

        $this->methodHandle("ApiTitle");
        $this->methodHandle("ApiAuthor");
        $this->methodHandle("ApiDesc");
        $this->methodHandle("ApiHeader");
        $this->methodHandle("ApiQuery");
        $this->methodHandle("ApiParam");
        $this->methodHandle("ApiType");
        $this->methodHandle("ApiError");
        $this->methodHandle("ApiSuccess");
        $this->methodHandle("ApiReturn");

        return $this->apps;
    }

    public function getMenu($appName, $className,$methodName): array
    {
        //获取类的公共数据
        $classData = [];
        $methodData = [];
        $menus = $this->getAllApiMenus();

        if (array_key_exists($appName, $menus)) {

            //判断$className是否存在
            if (array_key_exists($className, $menus[$appName]['child'])) {
                //判断$methodName是否存在
                if (array_key_exists($methodName, $menus[$appName]['child'][$className]['child'])) {
                    $classData = $menus[$appName]['child'][$className];
                    //返回$methodName的数据
                    $methodData = $menus[$appName]['child'][$className]['child'][$methodName];
                }
            }
        }


        $data = [];
        $data['title'] = $methodData['apiTitle']['value'] ?? $classData['apiTitle']['value'] ?? [];
        $data['author'] = $methodData['apiAuthor']['value'] ?? $classData['apiAuthor']['value'] ?? [];
        $data['desc'] = $methodData['apiDesc']['value'] ?? $classData['apiDesc']['value'] ?? [];
        $data['type'] = $methodData['apiType']['value'] ?? $classData['apiType']['value'] ?? [];

        $data['header'] = array_merge($methodData['apiHeader'] ?? [], $classData['apiHeader'] ?? []);
        $data['query'] = array_merge($methodData['apiQuery'] ?? [], $classData['apiQuery'] ?? []);
        $data['param'] = array_merge($methodData['apiParam'] ?? [], $classData['apiParam'] ?? []);


        $data['return'] = array_merge($methodData['apiReturn'] ?? [], $classData['apiReturn'] ?? []);

        $data['error'] = array_merge($methodData['apiError'] ?? [], $classData['apiError'] ?? []);
        $data['success'] = array_merge($methodData['apiSuccess'] ?? [], $classData['apiSuccess'] ?? []);

        //使用mountHandle方法,将$data中的mount属性挂载到$data中
        $data['error'] = $this->mountHandle($data['error'],$data['return']);
        $data['success'] = $this->mountHandle($data['success'],$data['return']);
        $routers = $this->routerHandle();
        //todo 如果不同的method不同的路由，这里没法区分，只能是相同的
        $router = $routers[$className . '@' . $methodName] ?? [];
        if ($router) {
            foreach ($router as $key => $value) {
                $data['methods'][] = $value['method'];
                $data['router'] = $value['route'] ?? "";
            }
        }
        return $data;
    }

    /**
     * 处理路由
     */
    private function routerHandle(): array
    {
        $routers = [];
        //从配置中获取所有的serverName
        $serverConfig = ApplicationContext::getContainer()->get(ConfigInterface::class)->get('server', []);
        if (! $serverConfig) {
            throw new InvalidArgumentException('At least one server should be defined.');
        }
        $serversConfig = $serverConfig['servers'] ?? [];
        $router = ApplicationContext::getContainer()->get(DispatcherFactory::class);
        foreach ($serversConfig as $server) {
            $r = $router->getRouter($server['name']);
            foreach ($r->getData() as $route) {
                foreach ($route as $method => $value) {
                    foreach ($value as $item) {
                        //$item类转数组
                        $item = json_decode(json_encode($item), true);
                        if (isset($item['callback'])) {
                            $item['method'] = $method;
                            if (is_array($item['callback'])) {
                                $routers[implode('@', $item['callback'])][] = $item;
                            }else{
                                $routers[$item['callback']][] = $item;
                            }
                        }
                    }
                }
            }
        }
        return $routers;
    }
    /**
     * 处理class
     */
    private function classHandle($className): void
    {
        //$className开头大写
        $data = AnnotationCollector::getClassesByAnnotation('Haozing\FastCore\Annotation\OpenDoc\\'.ucfirst($className));
        foreach ($data as $class => $doc) {
            // 判断$class 是否是Plugins、Internal、Apps开头，只有符合的才进行下一步
            if (str_starts_with($class, 'Plugins') || str_starts_with($class, 'Internal') || str_starts_with($class, 'Apps')) {
                // 如果开头是Apps。将$class按照\分割，取数组的前三个作为应用名称。其他取前两个
                $parts = explode('\\', $class);

                // 检查字符串是否以"Apps"开头
                if (str_starts_with($class, 'Apps')) {
                    // 如果是，取数组的前三个元素
                    $appName = implode('\\', array_slice($parts, 0, 3));
                } else {
                    // 如果不是，取数组的前两个元素
                    $appName = implode('\\', array_slice($parts, 0, 2));
                }
                //查看apps中是否存在appName
                if (array_key_exists($appName, $this->apps)) {
                    //设置多个的数据
                    $multiple = ['ApiHeader', 'ApiQuery','ApiParam', 'ApiReturn','ApiError','ApiSuccess'];
                    if (!isset($this->apps[$appName]['child'][$class][lcfirst($className)])) {
                        $this->apps[$appName]['child'][$class][lcfirst($className)] = [];
                    }
                    if (in_array($className, $multiple)) {
                        //如果存在，将$appTitle添加到child数组中
                        $this->apps[$appName]['child'][$class][lcfirst($className)] = $doc->getArray();
                    } else {
                        //如果存在，将$appTitle添加到child数组中
                        $this->apps[$appName]['child'][$class][lcfirst($className)] = $doc->toArray();
                    }
                }
            }
        }
    }

    /**
     * 处理method
     */
    private function methodHandle($className): void
    {
        //$className开头大写
        $data = AnnotationCollector::getMethodsByAnnotation('Haozing\FastCore\Annotation\OpenDoc\\'.ucfirst($className));
        foreach ($data as $item) {

            if (str_starts_with($item['class'], 'Plugins') || str_starts_with($item['class'], 'Internal') || str_starts_with($item['class'], 'Apps')) {
                // 如果开头是Apps。将$class按照\分割，取数组的前三个作为应用名称。其他取前两个
                $parts = explode('\\', $item['class']);

                // 检查字符串是否以"Apps"开头
                if (str_starts_with($item['class'], 'Apps')) {
                    // 如果是，取数组的前三个元素
                    $appName = implode('\\', array_slice($parts, 0, 3));
                } else {
                    // 如果不是，取数组的前两个元素
                    $appName = implode('\\', array_slice($parts, 0, 2));
                }
                //查看apps中是否存在appName
                if (array_key_exists($appName, $this->apps)) {
                    // 判断$item['class']在apps中是否存在，存在则添加到apps中
                    if (array_key_exists($item['class'], $this->apps[$appName]['child'])) {
                        //如果存在，将$item['class']和$item['method']添加到其中
                        //设置多个的数据
                        $multiple = ['ApiHeader', 'ApiQuery','ApiParam', 'ApiReturn','ApiError','ApiSuccess'];
                        //先判断$this->apps[$appName]['child'][$item['class']]['child'][$item['method']][lcfirst($className)]是否存在，不存在则创建
                        if (!isset($this->apps[$appName]['child'][$item['class']]['child'][$item['method']][lcfirst($className)])) {
                            $this->apps[$appName]['child'][$item['class']]['child'][$item['method']][lcfirst($className)] = [];
                        }

                        if (in_array($className, $multiple)) {
                            //如果存在，将$appTitle添加到child数组中
                            $this->apps[$appName]['child'][$item['class']]['child'][$item['method']][lcfirst($className)] = $item['annotation']->getArray();
                        } else {
                            //如果存在，将$appTitle添加到child数组中
                            $this->apps[$appName]['child'][$item['class']]['child'][$item['method']][lcfirst($className)] = $item['annotation']->toArray();
                        }
                    }
                }
            }
        }
    }

    /**
     * 处理挂载问题
     */
    private function mountHandle(array $responseData,array $returned): array
    {

        $data = [];
        // 循环data查找设置了$main参数的数组
        foreach ($responseData as $key => $value) {
            // 判断$value['main']是否为true
            if ($value['main'] === true) {
                // 如果是，将$key添加到$returned中
                $value['children'] = $returned;
                $value['childrenType'] = 'array';
            }
            $data[$key] = $value;
        }
        return $data;
    }
}